package com.lin.fengxiu.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @ClassName JwtToken
 * @Description TODO JWT令牌工具类，根据微信openid与用户信息生成.
 * @Description TODO 令牌的获取包括了：令牌的生成、令牌的验证两个阶段。
 * @Author wushaopei
 * @Date 2021/3/21 16:16
 * @Version 1.0
 */
@Component
public class JwtToken {

    /**
     *  盐值
     */
    private static String jwtKey;
    /**
     *  过期时间
     */
    private static Integer expiredTimeIn;

    private static Integer defaultScope = 8;

    @Value("${fengxiu.security.jwt-key}")
    public void setJwtKey(String jwtKey){
        JwtToken.jwtKey = jwtKey;
    }

    @Value("${fengxiu.security.token-expired-in}")
    public void setExpiredTimeIn(Integer expiredTimeIn){
        JwtToken.expiredTimeIn = expiredTimeIn;
    }

    /**
     * @Description TODO token 令牌校验
     * @param token
     * @return
     */
    public static Optional<Map<String,Claim>> getClaims(String token){
        DecodedJWT decodedJWT;
        //   算法定义了一个令牌是如何被签名和验证的。它可以用HMAC算法的原始值来实例化，也可以在RSA和ECDSA算法的情况下对密钥对或密钥提供程序进行实例化。创建后，该实例可用于令牌签名和验证操作。
        //在使用RSA或ECDSA算法时，只需要签署JWTs，就可以通过传递null值来避免指定公钥。当您需要验证JWTs时，也可以使用私钥进行操作
        Algorithm algorithm = Algorithm.HMAC256(JwtToken.jwtKey);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            decodedJWT = jwtVerifier.verify(token);
        }catch (JWTVerificationException e){
            return Optional.empty();
        }
        return Optional.of(decodedJWT.getClaims());
    }
    // 校验令牌合法性
    public static Boolean verifyToken(String token){
        try{
            Algorithm algorithm = Algorithm.HMAC256(JwtToken.jwtKey);
            JWTVerifier verifier = JWT.require(algorithm).build();
            verifier.verify(token);
        }catch (JWTVerificationException e){
            return false;
        }
        return true;
    }

    /**
     * @Description TODO 令牌获取的入口，这里不进行JWT令牌的具体逻辑编写，具体逻辑由 getToken() 功能负责
     * @param uid  userId
     * @param scope  权限分级数字
     * @return
     */
    public static String makeToken(Long uid, Integer scope){
        return JwtToken.getToken(uid, scope);
    }

    /**
     * @Description 不传入scope，默认等级为8
     * @param uid
     * @return
     */
    public static String makeToken(Long uid){
        return JwtToken.getToken(uid, JwtToken.defaultScope);
    }

    /**
     * @Description TODO 令牌生成的过程
     * @param uid userId
     * @param scope 权限分级数字
     * @return
     */
    private static String getToken(Long uid, Integer scope){
        // 使用 Algorithm 组装 JWT
        Algorithm algorithm = Algorithm.HMAC256(JwtToken.jwtKey);
        // 定义token过期时间
        Map<String,Date> map = JwtToken.calculateExpiredIssues();

        //当使用jwt.create()创建一个令牌时，您可以通过调用withClaim()来指定自定义声明，并同时传递名称和值。
        String token = JWT.create()
                .withClaim("uid", uid)
                .withClaim("scope",scope)
                .withExpiresAt(map.get("expiredTime"))
                .withIssuedAt(map.get("now"))
                .sign(algorithm);
        return token;

    }

    /**
     *
     * @return map, 返回的 map 包含有当前时间以及token的过期时间
     */
    private static Map<String,Date> calculateExpiredIssues(){
        Map<String, Date> map = new HashMap<>();
        Calendar calendar = Calendar.getInstance();
        Date now = calendar.getTime();
        calendar.add(Calendar.SECOND, JwtToken.expiredTimeIn);
        map.put("now",now);
        map.put("expiredTime", calendar.getTime());
        return map;
    }
}
